/*
 *Copyright (c) 2024 Black Sesame Technologies
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
*/

#ifndef C_LOCKFREE_LIST_H
#define C_LOCKFREE_LIST_H

#include "bst_ipc_config.h"

struct _lflist_node_t {
	void *data;
	struct _lflist_node_t *next;
};
#define lflist_node_t struct _lflist_node_t

struct _lflist_t {
#if !defined(__riscv)
	_Atomic(lflist_node_t *) head;
	_Atomic(lflist_node_t *) tail;
#else
	lflist_node_t *head;
	lflist_node_t *tail;
#endif
	lflist_node_t head_node;
	uint8_t initialized;
};
#define lflist_t struct _lflist_t

static inline void lflist_init(lflist_t *list)
{
	if (!list)
		return;

	list->head_node.next = NULL;
	ATOMIC_STORE(&(list->head), &list->head_node, __ATOMIC_RELAXED);
	ATOMIC_STORE(&list->tail, &list->head_node, __ATOMIC_RELAXED);
	list->initialized = 1;
}

static inline void lflist_enqueue(lflist_t *list, lflist_node_t *node)
{
	lflist_node_t *prevHead = NULL;

	if (!list || !node || list->initialized != 1)
		return;

	node->next = NULL;
	prevHead = ATOMIC_LOAD(&list->head, __ATOMIC_ACQUIRE);
#if defined(__riscv) || defined(__XTENSA__)
	ATOMIC_STORE(&list->head, node, __ATOMIC_RELEASE);
#else
	do {
		prevHead = ATOMIC_LOAD(&list->head, __ATOMIC_ACQUIRE);
	} while (!ATOMIC_COMPARE_EXCHANGE_STRONG(&list->head, &prevHead, node,
						 __ATOMIC_RELEASE,
						 __ATOMIC_RELAXED));
#endif
	prevHead->next = node;
}

static inline lflist_node_t *lflist_dequeue(lflist_t *list)
{
	lflist_node_t *tail = NULL;
	lflist_node_t *next = NULL;
	void *data = NULL;
	
	if(!list || list->initialized != 1)
		return NULL;

#if defined(__riscv) || defined(__XTENSA__)
	tail = ATOMIC_LOAD(&list->tail, __ATOMIC_ACQUIRE);
	next = tail->next;
	if (next == NULL)
		return NULL; // Queue is empty
	ATOMIC_STORE(&list->tail, next, __ATOMIC_RELEASE);
	data = next->data;
#else
	do {
		tail = ATOMIC_LOAD(&list->tail, __ATOMIC_ACQUIRE);
		next = tail->next;
		if (next == NULL)
			return NULL; // Queue is empty
		data = next->data;
	} while (!ATOMIC_COMPARE_EXCHANGE_STRONG(
		&list->tail, &tail, next, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
#endif

	tail->data = data;
	tail->next = NULL;
	return tail;
}

#endif
